#include "BasicEffect.h"
#include "Game.h"
#include <assert.h>
#include "ShadowMap.h"

void BasicEffect::Initialize()
{
	mDevice = CDirect3D::Instance()->GetDevice();
	mDeviceContext = CDirect3D::Instance()->GetDeviceContext();


	DWORD shaderFlags = D3D11_CREATE_DEVICE_DEBUG ;


	ID3D10Blob* errorMessage;
	ID3D10Blob* compileShader;
	HRESULT result = D3DX11CompileFromFile(L"Contents\\Shaders\\BasicEffect.fx", 0, 0, 0, 
											"fx_5_0", shaderFlags, 0, 0, 
											&compileShader, &errorMessage, 0);

	char* compileErrors = (char*)(errorMessage->GetBufferPointer());

	assert(!FAILED(result));

	D3DX11CreateEffectFromMemory(compileShader->GetBufferPointer(), compileShader->GetBufferSize(), 0, mDevice, &mEffect);

	mTechnique = mEffect->GetTechniqueByName("BasicEffect");
	mTechniqueBillboard = mEffect->GetTechniqueByName("BillboardEffect");
	mFxMatrixWorld = mEffect->GetVariableByName("gWorldMatrix")->AsMatrix();
	mFxMatrixView = mEffect->GetVariableByName("gViewMatrix")->AsMatrix();
	mFxMatrixProjection = mEffect->GetVariableByName("gProjectionMatrix")->AsMatrix();
	mFxEyesPosition = mEffect->GetVariableByName("gEyePosW");
	mFxMaterial = mEffect->GetVariableByName("gMaterial");
	mFxDirectionalLight = mEffect->GetVariableByName("gDirLight");
	mFxEnableDirLightDirLight = mEffect->GetVariableByName("gEnableDirLight");
	mFxListPointsLights = mEffect->GetVariableByName("gListPointsLights");
	mFxPointLightCount = mEffect->GetVariableByName("gPointLightCount");
	mFxShadowMatrixView = mEffect->GetVariableByName("gShadowViewMatrix")->AsMatrix();
	mFxShadowMatrixProjection = mEffect->GetVariableByName("gShadowProjectionMatrix")->AsMatrix();

	InitInputLayout();
	InitInputLayoutBillboard();
	InitBlendState();
	InitSamplerState();

	mPointsLightsManager = new PointsLightsManager();
	mPointsLightsManager->SetBasicEffect(this);
	mPointsLightsManager->Initialize();
}

void BasicEffect::InitSamplerState()
{
	D3D11_SAMPLER_DESC samplerDesc;
	// Create a texture sampler state description.
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.BorderColor[0] = 0;
	samplerDesc.BorderColor[1] = 0;
	samplerDesc.BorderColor[2] = 0;
	samplerDesc.BorderColor[3] = 0;
	samplerDesc.MinLOD = 0;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	HRESULT result = mDevice->CreateSamplerState(&samplerDesc, &mSampleState);

}

void BasicEffect::InitBlendState()
{
	ZeroMemory(&mBlendState, sizeof(ID3D11BlendState));
 
	D3D11_BLEND_DESC blendStateDesc; 
	ZeroMemory(&blendStateDesc, sizeof(D3D11_BLEND_DESC));
	blendStateDesc.AlphaToCoverageEnable = FALSE;
	blendStateDesc.IndependentBlendEnable = FALSE;        
	blendStateDesc.RenderTarget[0].BlendEnable = TRUE;
	blendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	blendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	blendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	blendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
	blendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA;
	blendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendStateDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	HRESULT result = mDevice->CreateBlendState( &blendStateDesc, &mBlendState );
	assert(!FAILED(result));
	mDeviceContext->OMSetBlendState(mBlendState, NULL, 0xFFFFFF);
}

void BasicEffect::InitInputLayout()
{
	HRESULT result;
	unsigned int numElements;
	D3D11_INPUT_ELEMENT_DESC polygonLayout[4];

		// Create the vertex input layout description.
	// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
	polygonLayout[0].SemanticName = "POSITION";
	polygonLayout[0].SemanticIndex = 0;
	polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[0].InputSlot = 0;
	polygonLayout[0].AlignedByteOffset = 0;
	polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[0].InstanceDataStepRate = 0;

	polygonLayout[1].SemanticName = "TEXCOORD";
	polygonLayout[1].SemanticIndex = 0;
	polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[1].InputSlot = 0;
	polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[1].InstanceDataStepRate = 0;

	polygonLayout[2].SemanticName = "TEXCOORD";
	polygonLayout[2].SemanticIndex = 1;
	polygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[2].InputSlot = 0;
	polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[2].InstanceDataStepRate = 0;

	polygonLayout[3].SemanticName = "FOG";
	polygonLayout[3].SemanticIndex = 0;
	polygonLayout[3].Format = DXGI_FORMAT_R32_FLOAT;
	polygonLayout[3].InputSlot = 0;
	polygonLayout[3].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[3].InstanceDataStepRate = 0;
	// Get a count of the elements in the layout.
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

	// Create the vertex input layout.
	D3DX11_PASS_SHADER_DESC effectVsDesc;
	mTechnique->GetPassByIndex(0)->GetVertexShaderDesc(&effectVsDesc);
	D3DX11_EFFECT_SHADER_DESC  effectVsDesc2;
	effectVsDesc.pShaderVariable->GetShaderDesc(effectVsDesc.ShaderIndex, &effectVsDesc2);
	const void *vsCodePtr = effectVsDesc2.pBytecode;
	unsigned vsCodeLen = effectVsDesc2.BytecodeLength;

	result = mDevice->CreateInputLayout(polygonLayout, numElements, vsCodePtr, vsCodeLen, &mInputLayout);
	
	SetLightDirLightEnable();

	assert(!FAILED(result));
}

void BasicEffect::InitInputLayoutBillboard()
{
	HRESULT result;
	unsigned int numElements;
	D3D11_INPUT_ELEMENT_DESC polygonLayout[5];

		// Create the vertex input layout description.
	// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
	polygonLayout[0].SemanticName = "POSITION";
	polygonLayout[0].SemanticIndex = 0;
	polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[0].InputSlot = 0;
	polygonLayout[0].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[0].InstanceDataStepRate = 0;

	polygonLayout[1].SemanticName = "SIZE";
	polygonLayout[1].SemanticIndex = 0;
	polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[1].InputSlot = 0;
	polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[1].InstanceDataStepRate = 0;

	polygonLayout[2].SemanticName = "FOG";
	polygonLayout[2].SemanticIndex = 0;
	polygonLayout[2].Format = DXGI_FORMAT_R32_FLOAT;
	polygonLayout[2].InputSlot = 0;
	polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[2].InstanceDataStepRate = 0;

	polygonLayout[3].SemanticName = "TEXCOORD";
	polygonLayout[3].SemanticIndex = 0;
	polygonLayout[3].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[3].InputSlot = 0;
	polygonLayout[3].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[3].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[3].InstanceDataStepRate = 0;

	polygonLayout[4].SemanticName = "TEXCOORD";
	polygonLayout[4].SemanticIndex = 1;
	polygonLayout[4].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[4].InputSlot = 0;
	polygonLayout[4].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[4].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[4].InstanceDataStepRate = 0;

	// Get a count of the elements in the layout.
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

	// Create the vertex input layout.
	D3DX11_PASS_SHADER_DESC effectVsDesc;
	mTechniqueBillboard->GetPassByIndex(0)->GetVertexShaderDesc(&effectVsDesc);
	D3DX11_EFFECT_SHADER_DESC  effectVsDesc2;
	effectVsDesc.pShaderVariable->GetShaderDesc(effectVsDesc.ShaderIndex, &effectVsDesc2);
	const void *vsCodePtr = effectVsDesc2.pBytecode;
	unsigned vsCodeLen = effectVsDesc2.BytecodeLength;

	result = mDevice->CreateInputLayout(polygonLayout, numElements, vsCodePtr, vsCodeLen, &mBillboardInputLayout);
	
	SetLightDirLightEnable();

	assert(!FAILED(result));
}

PointsLightsManager* BasicEffect::GetPointsLightsManager()
{
	return mPointsLightsManager;
}

void BasicEffect::SetTexture(Texture * pTexture)
{
	mTexture = pTexture;
}

void BasicEffect::SetShadowTexture(ID3D11ShaderResourceView* pTexture)
{
	mShadowTexture = pTexture;
}

void BasicEffect::SetTransformsMatrices(const XMFLOAT4X4 &pWorldMatrix, const XMFLOAT4X4 &pViewMatrix, const XMFLOAT4X4 &pProjectionMatrix)
{
	mFxMatrixWorld->SetMatrix(reinterpret_cast<const float*>(&pWorldMatrix));
	mFxMatrixView->SetMatrix(reinterpret_cast<const float*>(&pViewMatrix));
	mFxMatrixProjection->SetMatrix(reinterpret_cast<const float*>(&pProjectionMatrix));

	struct DirectionalLight light;
	light.Ambient = XMFLOAT4(1.0f,1.0f,1.0f,1);
	light.Direction = Game::Instance()->GetLightOrientation();
	mFxDirectionalLight->SetRawValue(&light, 0, sizeof(DirectionalLight));
}

void BasicEffect::SetLightDirLightEnable()
{
	bool enableDirLightDirLight = true;
	mFxEnableDirLightDirLight->SetRawValue(&enableDirLightDirLight, 0, sizeof(enableDirLightDirLight));
}

void BasicEffect::SetLightDirLightDisable()
{
	bool enableDirLightDirLight = false;
	mFxEnableDirLightDirLight->SetRawValue(&enableDirLightDirLight, 0, sizeof(enableDirLightDirLight));
}

void BasicEffect::SetEyesPosition(const XMFLOAT3 &pEyesPosition)
{
	mFxEyesPosition->SetRawValue(&pEyesPosition, 0, sizeof(XMFLOAT3));
}

void BasicEffect::SetPointLightList(PointLight* pListPointsLights, int pPointLightCount)
{
	mFxPointLightCount->SetRawValue(&pPointLightCount, 0, sizeof(int));
	mFxListPointsLights->SetRawValue(pListPointsLights, 0, sizeof(PointLight) * pPointLightCount);
}

void BasicEffect::Render(int pIndexCount)
{
	mDeviceContext->IASetInputLayout(mInputLayout);	
	mTechnique->GetPassByIndex(0)->Apply(0, mDeviceContext);
	mDeviceContext->PSSetSamplers(0, 1, &mSampleState);
	ID3D11ShaderResourceView* texture = mTexture->GetTexture();
	mDeviceContext->PSSetShaderResources(0, 1, &texture);
	mDeviceContext->PSSetShaderResources(1, 1, &mShadowTexture);
	mDeviceContext->DrawIndexed(pIndexCount, 0, 0);
}

void BasicEffect::RenderBillboard(int pIndexCount)
{
	mDeviceContext->IASetInputLayout(mBillboardInputLayout);	
	mTechniqueBillboard->GetPassByIndex(0)->Apply(0, mDeviceContext);
	mDeviceContext->PSSetSamplers(0, 1, &mSampleState);
	ID3D11ShaderResourceView* texture = mTexture->GetTexture();
	mDeviceContext->PSSetShaderResources(0, 1, &texture);
	mDeviceContext->PSSetShaderResources(1, 1, &mShadowTexture);
	mDeviceContext->Draw(pIndexCount, 0);
}

ID3D11InputLayout* BasicEffect::GetBillboardInputLayout()
{
	return mBillboardInputLayout;
}
